﻿using System.Reflection;
using Google.Protobuf;
using Google.Protobuf.Reflection;
using Proto;
using Serilog;

namespace MoonFramework.Tool
{
	public static class ProtoTool
	{
		//用于网络传输
		public static byte[] Serialize<T>(T message) where T : IMessage
				=> message.ToByteArray();

		public static T Deserialize<T>(byte[] bytes) where T : class, IMessage, new()
		{
			T message = new();
			return message.Descriptor.Parser.ParseFrom(bytes) as T;
		}

		public static T Deserialize<T>(T message, byte[] data) where T : class, IMessage, new()
			=> message.Descriptor.Parser.ParseFrom(data) as T;

		public static T Deserialize<T>(MessageDescriptor descriptor, byte[] data) where T : class, IMessage, new()
			=> descriptor.Parser.ParseFrom(data) as T;
		
		//kv存储反射出的所有的IMessage类型
		private static Dictionary<string, Type> _register = new();
		private static List<Type> ToTypeDic = new();
		private static Dictionary<Type, int> ToCodeDic = new();
		static ProtoTool()
		{
			List<string> list = new();
			var q = from t in Assembly.GetEntryAssembly()?.GetTypes() select t;
			q.ToList().ForEach(t =>
			{
				if (typeof(IMessage).IsAssignableFrom(t))
				{
					var descriptor = t.GetProperty("Descriptor")?.GetValue(t) as MessageDescriptor;
					_register.TryAdd(descriptor?.FullName, t);
					list.Add(descriptor.FullName);
				}
			});
			//按照字符串长度排序
			list.Sort((x,y)=> 
				x.Length != y.Length ? x.Length - y.Length : string.Compare(x, y, StringComparison.Ordinal));
			
			int i = 0;
			foreach (var fullName in list)
			{
				var t = _register[fullName];
				ToTypeDic.Add(t);
				ToCodeDic.Add(t, i++);
			}
		}

		public static int ToCode(Type type) => ToCodeDic[type];
		public static Type ToType(int code) => ToTypeDic[code];
		
		//打包
		public static Proto.Package Pack(IMessage message)
		{
			Package package = new();
			package.FullName = message.Descriptor.FullName;
			package.Data = message.ToByteString();
			return package;
		}

		//拆包
		public static IMessage UnPack(Proto.Package package)
		{
			string fullName = package.FullName;
			if (_register.TryGetValue(fullName, out var t))
			{
				MessageDescriptor descriptor = t.GetProperty("Descriptor").GetValue(t) as MessageDescriptor;
				return descriptor.Parser.ParseFrom(package.Data);
			}
			return null;
		}

		public static IMessage ParseFrom(int typeCode, byte[] buffer, int offset, int len)
		{
			Type t = ProtoTool.ToType(typeCode);
			MessageDescriptor descriptor = t.GetProperty("Descriptor").GetValue(t) as MessageDescriptor;
			var msg = descriptor.Parser.ParseFrom(buffer, offset, len);
			Log.Information("解析新消息：code = {0} - {1}",typeCode, msg);
			return msg;
		}
	}
}